/**
 * Project Name:kany-utils
 * File Name:HttpClientManager.java
 * Package Name:me.kany.utils.httpclient
 * Date:2016年2月24日上午10:23:03
 * Copyright (c) 2016, Jason.Wang All Rights Reserved.
 *
*/

package me.kany.utils.httpclient;

import java.util.HashMap;
import java.util.Map;

import javax.net.ssl.SSLContext;

import org.apache.http.client.HttpClient;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;

/**
 * ClassName:HttpClientManager <br/>
 * Function: HttpClient客户端管理工具，用户获取及创建HttpClient对象. <br/>
 * Reason: 当前需要依赖HttpClient4.5.1于HttpCore4.4.3. <br/>
 * Date: 2016年2月24日 上午10:23:03 <br/>
 * 
 * @author Jason.Wang
 * @version
 * @since JDK 1.7
 * @see
 */
public class HttpClientManager {
    private static PoolingHttpClientConnectionManager defaultSSLConnManager;
    private static final Map<KeyStoreMaterial, PoolingHttpClientConnectionManager> sslConnManager = new HashMap<KeyStoreMaterial, PoolingHttpClientConnectionManager>();

    private static final Object sslLock = new Object();

    static {
        try {
            SSLContext sslcontext = SSLContexts.custom().build();
            defaultSSLConnManager = getSSLContextConnManager(sslcontext);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static PoolingHttpClientConnectionManager getSSLConnManager(KeyStoreMaterial keyStore) {
        if (keyStore == null) {
            return defaultSSLConnManager;
        }
        PoolingHttpClientConnectionManager poolingHttpClientConnectionManager = sslConnManager.get(keyStore);
        if (null == poolingHttpClientConnectionManager) {
            synchronized (sslLock) {
                poolingHttpClientConnectionManager = sslConnManager.get(keyStore);
                if (null == poolingHttpClientConnectionManager) {
                    poolingHttpClientConnectionManager = initConnManager(keyStore);
                    sslConnManager.put(keyStore, poolingHttpClientConnectionManager);
                }
            }
        }
        return poolingHttpClientConnectionManager;
    }
    
    protected static PoolingHttpClientConnectionManager initConnManager(String path, String password) {
        PoolingHttpClientConnectionManager connManager = null;
        try {
            // SSLContext 可以加载客户端证书，或者不加载客户端证书
            // 需要加载服务器证书
            SSLContext sslcontext = null;
            if ((null != path && "" != path) || (null != password && "" != password)) {
                sslcontext = SSLContexts.custom().build();
            } else {
                KeyStoreMaterial keyStore = KeyStoreUtil.getKeyStore(path, password);
                if (keyStore == null) {
                    throw new RuntimeException("keyStore not found for path:" + path + ",password=" + password);
                }
                sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore.getKeyStore(), keyStore.getPassword().toCharArray()).build();
            }
            connManager = getSSLContextConnManager(sslcontext);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connManager;
    }
    
    /**
     * 
     * initConnManager:初始化连接池. <br/>
     *
     * @author Jason.Wang
     * @param keyStore
     * @return
     * @since JDK 1.7
     */
    protected static PoolingHttpClientConnectionManager initConnManager(KeyStoreMaterial keyStore) {
        PoolingHttpClientConnectionManager connManager = null;
        try {
            SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore.getKeyStore(), keyStore.getPassword().toCharArray()).build();
            connManager = getSSLContextConnManager(sslcontext);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connManager;
    }
    
    /**
     * 
     * getSSLContextConnManager:根据SSLContext初始化一个连接管理器. <br/>
     *
     * @author Jason.Wang
     * @param sslcontext
     * @return
     * @since JDK 1.7
     */
    private static PoolingHttpClientConnectionManager getSSLContextConnManager(SSLContext sslcontext) {
        PoolingHttpClientConnectionManager connManager = null;
        // 服务器验证策略使用DNS域名解析
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());

        // Create a registry of custom connection socket factories for supported
        // protocol schemes.
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create().register("http", PlainConnectionSocketFactory.INSTANCE).register("https", sslsf).build();
        connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connManager.setMaxTotal(200);
        connManager.setDefaultMaxPerRoute(20);
        connManager.setValidateAfterInactivity(1000);
        return connManager;
    }
    
    /**
     * 
     * getSSLHttpClient:client/server certification<br/>
     * 
     * @author Jason.Wang
     * @param path
     * @param password
     * @return
     * @throws Exception
     * @since JDK 1.7
     */
    public static HttpClient getSSLHttpClient(String path, String password) throws Exception {
        KeyStoreMaterial keyStore = KeyStoreUtil.getKeyStore(path, password);
        return getSSLHttpClient(keyStore);
    }

    /**
     * getSSLHttpClient:获取默认的SSL,不携带客户端证书<br/>
     * 
     * @author Jason.Wang
     * @return
     * @throws Exception
     * @since JDK 1.7
     */
    public static HttpClient getSSLHttpClient() throws Exception {
        return getSSLHttpClient("", "");
    }

    /**
     * 
     * getSSLHttpClient:根据keyStore获取一个httpclient. <br/>
     *
     * @author Jason.Wang
     * @param keyStore
     * @return
     * @throws Exception
     * @since JDK 1.7
     */
    public static HttpClient getSSLHttpClient(KeyStoreMaterial keyStore) throws Exception {
        HttpClient client = null;
        PoolingHttpClientConnectionManager connManager = getSSLConnManager(keyStore);
        if (connManager == null) {
            return null;
        }
        client = HttpClients.custom().setConnectionManager(connManager).build();
        return client;
    }
}
